#pragma once
#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include "Socket.hpp"
#include "Epoller.hpp"
#include "Log.hpp"

const int defaultport = 8888;
const int defaultbacklog = 32;

class EpollServer
{
private:
    static const int maxevents = 64;

    int Recv(int fd, std::string* out)
    {
        char buffer[1024];
        int n = recv(fd, buffer, sizeof(buffer), 0);
        if(n > 0)
        {
            buffer[n] = 0;
            *out += buffer;
        }

        return n;
    }

    void HandlerEvents(int n)
    {
        for(int i = 0; i < n; i++)
        {
            if(_ev[i].events & EPOLLIN)
            {
                int fd = _ev[i].data.fd;
                if(fd == _listensock->GetSockFd())
                {
                    // 有新的连接到来，将它继续交给epoll模型接管
                    std::string ip;
                    uint16_t port;
                    int newsock = _listensock->BuildServerSock(&ip, &port);
                    if(newsock < 0)
                    {
                        LOG(Error, "failed to obtain connection\n");
                        continue;
                    }
                    LOG(Error, "successfully obtained connection, fd: %d\n", newsock);
                    _epoller->Add(newsock, EPOLLIN);
                }
                else
                {
                    // 普通文件描述符的读条件就绪，在这里不做正确处理，为了方便，只进行大致的处理
                    string msg;
                    int n = Recv(fd, &msg);
                    if(n > 0)
                    {
                        cout << "client say# " << msg << endl;
                        send(fd, string("hello" + msg).c_str(), string("hello" + msg).size(), 0);
                    }
                    else
                    {
                        // 不管读取失败还是读到对端关闭，最终都得让该文件描述符关闭，并且从epoll模型中移除，所以放在一个判断分支内
                        if(n == -1)
                        {
                            LOG(Error, "fail to read file %d, %s, %d\n", fd, strerror(errno), errno);
                        }
                        else
                        {
                            LOG(Info, "close to read file %d\n", fd);
                        }

                        _epoller->Del(fd);
                        close(fd);
                    }

                }
            }
        }
    }
public:
    EpollServer(uint16_t port = defaultport)
        : _port(port), _listensock(new TcpSocket()), _epoller(new Epoller()), _isrunning(false)
    {
    }

    void Init()
    {
        // 1. 初始化listensock
        _listensock->BuildListenSock(_port, defaultbacklog);
        // 2. 初始化epoll模型
        _epoller->Init();
        // 将_listensock加入到epoll模型中
        _epoller->Add(_listensock->GetSockFd(), EPOLLIN);
    }

    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            int n = _epoller->Wait(_ev, maxevents);
            switch (n)
            {
            case -1:
                LOG(Error, "epoll wait error, %s, %d\n", strerror(errno), errno);
                break;
            case 0:
                LOG(Info, "waiting for timeout\n");
                break;
            default:
                HandlerEvents(n);
                break;
            }
        }
        _isrunning = false;
    }

    void Stop()
    {
        _isrunning = false;
    }

    ~EpollServer()
    {
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;

    bool _isrunning;

    std::unique_ptr<Epoller> _epoller;
    struct epoll_event _ev[maxevents];
};